/*
 * Copyright (C) 2008 GeoScheduler Team, as stated on <http://www.geoscheduler.org/authors>.
 * 
 * This file is part of GeoScheduler.
 * 
 * GeoScheduler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GeoScheduler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with GeoScheduler. If not, see <http://www.gnu.org/licenses/>.
 */
package org.geoscheduler.security.authenticate.impl;

import java.io.IOException;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.geoscheduler.exceptions.GSAuthenticationException;
import org.geoscheduler.model.Account;
import org.geoscheduler.model.Account.AccountType;
import org.geoscheduler.security.authenticate.Authenticator;

/**
 * Google account authenticator provides ability to check {@link Account} credentials
 * against Google's authentication service.
 *
 * @author srakyi
 */
public class AuthGoogleAccount implements Authenticator {
	
	// FIXME [srakyi] to be moved somewhere else ..
	public static String APPLICATION_ID = "GeoScheduler-0.1-alpha";
	
	/**
	 * Hard wired {@link AccountType}.
	 * 
	 * @see org.geoscheduler.security.authenticate.Authenticator#getApplicableTo()
	 */
	public AccountType getApplicableTo() {
		return AccountType.GOOGLE_ACCOUNT;
	}
	
	/**
	 * @see org.geoscheduler.security.authenticate.Authenticator#checkCredentials(org.geoscheduler.model.Account)
	 */
	public boolean checkCredentials(Account account) throws GSAuthenticationException {
		AuthData ad = login(account.getLogin(), account.getPasswd());
		
		return ad.isLogged();
	}

	/**
	 * Static method to check given e-mail (AKA Google account login) and password against google.com.
	 * 
	 * @param email	E-mail to be checked.
	 * @param passwd	Password that should conform to given e-mail.
	 * 
	 * @return	{@link AuthData} with data returned by Google.
	 * 
	 * @throws GSAuthenticationException
	 */
	public AuthData login(String email, String passwd) throws GSAuthenticationException {
		HttpMethod method = new PostMethod("https://www.google.com/accounts/ClientLogin");
		method.addRequestHeader(new Header("Content-type", "application/x-www-form-urlencoded"));
		method.setQueryString(new NameValuePair[] {
				new NameValuePair("accountType", "HOSTED_OR_GOOGLE"),
				new NameValuePair("Email", email),
				new NameValuePair("Passwd", passwd),
				new NameValuePair("service", "xapi"),
				new NameValuePair("source", APPLICATION_ID)}
		);
		
		HttpClient client = new HttpClient();
		
		try {
			int returnCode = client.executeMethod(method);
			return new AuthData(returnCode, method.getResponseBody());
		} catch (HttpException e) {
			throw new GSAuthenticationException(e);
		} catch (IOException e) {
			throw new GSAuthenticationException(e);
		}
	}
	
	/**
	 * Data class with information returned by Google authentication server.
	 *
	 * @author srakyi
	 */
	public static class AuthData {
		private String Auth;
		private String SID;
		private String LSID;
		private String Error;
		
		private boolean logged;
		
		public AuthData() {
			super();
			
			this.logged = false;
		}

		public AuthData(int returnCode, byte[] response) {
			super();

			if (returnCode == 200) {
				this.logged = true;
			} else {
				this.logged = false;
				
				// this should be done more generally
				String resp = new String(response);
				if (resp.startsWith("Error=")) {
					this.Error = resp.replaceFirst("Error=", "");
				}
			}
			
			// FIXME [srakyi] parse response body ..
		}

		public String getAuth() {
			return this.Auth;
		}

		public String getSID() {
			return this.SID;
		}

		public String getLSID() {
			return this.LSID;
		}
		
		public String getError() {
			return this.Error;
		}

		public boolean isLogged() {
			return logged;
		}
	}
}
